package br.com.felinelayer.server.application;

import br.com.felinelayer.common.FelLogger;
import br.com.felinelayer.common.Message;
import br.com.felinelayer.common.Protocol;
import br.com.felinelayer.server.ClientServer;
import java.util.HashMap;

/**
 * Application
 */
public abstract class Application<T extends Application> implements Runnable {

    private ClientServer client;
    private Frame currentFrame;
    private String applicationName = "";

    public T setApplicationName( String applicationName ) {
        this.applicationName = applicationName;
        return (T) this;
    }

    public String getApplicationName() {
        return applicationName;
    }

    public T setClient( ClientServer client ) {
        this.client = client;
        return (T) this;
    }

    protected T showMessage( String message ) {
        boolean wasInBatch = unloadBatch();
        Message c = new Message( Protocol.MSG_NAME_SHOW_MESSAGE );
        c.setValueByName( Protocol.TEXT_SHOW_MESSAGE, message );
        inquire( c );
        if (wasInBatch)
            beginBatch();
        return (T) this;
    }

    protected String showInput( String message ) {
        boolean wasInBatch = unloadBatch();
        Message c = new Message( Protocol.MSG_NAME_SHOW_INPUT );
        c.setValueByName( Protocol.TEXT_SHOW_MESSAGE, message );
        c = inquire( c );
        String response = "";
        if ( c.isPropertyExists( Protocol.SHOW_INPUT_RESPONSE ) )
            response = c.getValueByName( Protocol.SHOW_INPUT_RESPONSE );
        if (wasInBatch)
            beginBatch();
        return response;
    }

//    public T firePropertyChange( Message message ) {
//        inquire( message );
//        return (T) this;
//    }

    public T beginBatch() {
        client.beginBatch();
        return (T) this;
    }

    public T endBatch() {
        client.endBatch();
        return (T) this;
    }

    protected T sendMessage( Message message ) {
        client.sendMessage( message );
        return (T) this;
    }

    protected Message inquire( Message message ) {
        return client.inquire( message );
    }
    
    protected boolean inBacth() {
        return client.inBacth();
    }

    public boolean unloadBatch() {
        boolean wasBatching = inBacth();
        if (wasBatching)
            endBatch();        
        return wasBatching;
    }

    public void messageReceived( Message message ) throws Exception {
        beginBatch();
        doMessageReceived( message );
        //System.out.println( message );
        // FelLogger.log( message.toString() );
        // If is event command
        if ( message.getName().equalsIgnoreCase( Protocol.MSG_NAME_EVENT ) ) {
            String event = message.getValueByName( Protocol.EVENT_NAME );
            int id = message.getIntByName( Protocol.EVENT_COMPONENT_ID );
            Component c = getComponentById( id );
            if (c == null)
                throw new Exception("Application.messageReceived() Component id " + id + " not found!");
            // 
            if ( message.isPropertyExists( Protocol.PROPERTY_NAME_TEXT ) ) {
                c.setUpdateFromOtherSide( true );
                c.setText( message.getValueByName( Protocol.PROPERTY_NAME_TEXT ) );
                c.setUpdateFromOtherSide( false );
            }
            // Fire event managment
            doEventReceived( c, event );
            c.messageReceived(message);
        }
        endBatch();
    }
    private int componentId = 0;
    private HashMap<Integer, Component> components = new HashMap<>();

    public Component getComponentById( int id ) {
        return components.get( new Integer( id ) );
    }
    
    @SuppressWarnings( "element-type-mismatch" )
    public Application<T> removeComponent(Component component) {
        components.remove( component );
        return this;
    }

    private <T extends Component> T addComponent( T component ) {
        component.application = this;
        components.put( new Integer( ++componentId ), component );
        component.setId( componentId );
        Message c = new Message( Protocol.MSG_NAME_CREATE_COMPONENT );
        c.setValueByName( Protocol.COMPONENT_CLASS, component.getComponentClassName());
//        if ( component instanceof Frame )
//            c.setValueByName( Protocol.COMPONENT_CLASS, Protocol.COMPONENT_FRAME );
//        if ( component instanceof Button )
//            c.setValueByName( Protocol.COMPONENT_CLASS, Protocol.COMPONENT_BUTTON );
//        if ( component instanceof ComboBox )
//            c.setValueByName( Protocol.COMPONENT_CLASS, Protocol.COMPONENT_COMBOBOX );
//        if ( component instanceof Edit )
//            c.setValueByName( Protocol.COMPONENT_CLASS, Protocol.COMPONENT_EDIT );
//        if ( component instanceof Label )
//            c.setValueByName( Protocol.COMPONENT_CLASS, Protocol.COMPONENT_LABEL );
        c.setValueByName( Protocol.COMPONENT_ID, componentId );
        // If isn´t frame and don´t have current frame then defines frame id
        if ( !( component instanceof Frame ) && ( currentFrame != null ) )
            c.setValueByName( Protocol.PARENT_FRAME_ID, currentFrame.getId() );
        // Send message and wait response
        // TODO: WHY MUST WAIT????
        client.inquire( c );
        return component;
    }

    public Frame addFrame() {
        Frame c = new Frame();
        currentFrame = c;
        return addComponent( c );
    }

    public Button addButton() {
        return addComponent( new Button() );
    }

    public ComboBox addComboBox() {
        return addComponent( new ComboBox() );
    }

    public Edit addEdit() {
        return addComponent( new Edit() );
    }
    
    public Grid addGrid() {
        return addComponent( new Grid() );
    }

    public Label addLabel() {
        return addComponent( new Label() );
    }
    protected boolean stopAuto = true;

    @Override
    public void run() {
        beginBatch();
        doStart();
        endBatch();
        if ( stopAuto )
            stop();
    }

    protected abstract void doStart();

    protected abstract void doEventReceived( Component sender, String event );

    protected abstract void doMessageReceived( Message message );

    protected void stop() {
        client.disconnect();
    }
}
